#region Imported Namespaces
using System;
using System.Text;
using System.Collections;
using System.Data.SqlClient;
using System.Data;

using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using BO = PHSRAG.Insight21.BusinessObjects;
using PHSRAG.Insight21.ScriptEngine;
using PHSRAG.Insight21.FormsManagement.Serialization;

#endregion

namespace PHSRAG.Insight21.FormsManagement
{
	#region Class QuestionnaireVersion
	/// <summary>
	/// QuestionnaireVersion abstracts the notion of a specific instance of a questionnaire. 
	/// Whenever there is a significant change as defined in the Techinical Design Document
	/// in the questionnaire, a new version is created. 
	/// 
	/// Among all the versions of a questionnaire, only one can be active at any point of time.
	/// The active version is the one used if a questionnaire has to be answered for a Protocol.
	/// If a version is made active, then it is marked as published/deployed in the database. A version which
	/// is published is assumed to be used in a protocol and hence no modifications to questions or their
	/// relationships are allowed.
	/// 
	/// A QuestionnaireVersion object holds the details of a protocol questionnaire's version and 
	/// holds a collection of questionnaire items, documents, studypersons and scripts
	/// </summary>
	public class QuestionnaireVersion : BO.SmartBusinessObject, ICloneable
	{

		#region Constants
		private const string QuestionnaireVersionKey = "QuestionnaireVersion_F206F345-CA81-428a-AA85-5304EDA43E50";

		private const string ColumnNameDescription = "Description";
		private const string ColumnNameVersion = "Version";
		private const string ColumnNameActive = "Active";
		private const string ColumnNamePublished = "Published";
		private const string ColumnNameQuestionnaireId = "QuestionnaireId";
		private const string ColumnNameId = "Id";
		private const string ColumnNameParentName = "ParentName";
		private const string ColumnNameOrder = "Order";
		#endregion

		#region Instance variables (private)
		private int version;
		private bool isActive;
		private int questionnaireID;
		private string description;
		private bool isDeployed;
		private ArrayList items;
		private ArrayList documents;
		private Hashtable scripts;
		private DataTable itemsForForm;
		private Questionnaire questionnaire;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new QuestionnaireVersion object based on the Cache Policy passed in as a parameter. 
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		public QuestionnaireVersion(ICachePolicy cachePolicy)
			: base(cachePolicy)
		{
		}


		/// <summary>
		/// Constructs a new QuestionnaireVersion object based on the Cache Policy passed in as a parameter. 
		/// All the instance variables will be set to the corresponding values specified in the Data Row
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="dataRow">DataRow holding the values to be assigned to instance variables</param>
		public QuestionnaireVersion(ICachePolicy cachePolicy, DataRow dataRow)
			: base(cachePolicy)
		{
			PopulateDataMembers(dataRow);
		}

		#endregion

		#region ICloneable
		object ICloneable.Clone()
		{
			return Clone();
		}
		#endregion

		public QuestionnaireVersion Clone()
		{
			QuestionnaireVersion clone;
			try
			{
				clone = (QuestionnaireVersion)MemberwiseClone();
				clone.Id = 0;
				clone.IsClone = true;
				clone.Dirty = true;

				clone.items = new ArrayList();
				foreach (QuestionnaireItem item in Items)
				{
					clone.items.Add(item.Clone());
				}

				clone.documents = new ArrayList();
				foreach (QuestionDocument document in Documents)
				{
					clone.documents.Add(document.Clone());
				}

				clone.scripts = new Hashtable();
				foreach (Script script in Scripts.Values)
				{
					Script cloneScript = script.Clone();
					clone.scripts.Add(cloneScript.Type, cloneScript);
				}
			}
			catch (Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog(
					"QuestionnaireVersion.Clone", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw;
			}

			return clone;
		}

		#region Properties
		/// <summary>
		/// Gets or sets the version for the questionnaire version
		/// </summary>
		public int Version
		{
			get { return version; }
			set { SetDirty(ref version, value); }
		}


		/// <summary>
		/// Gets or sets whether the questionnaire version is active
		/// </summary>
		public bool IsActive
		{
			get { return isActive; }
			set { SetDirty(ref isActive, value); }
		}


		/// <summary>
		/// Gets or sets the questionnaire id for the questionnaire version
		/// </summary>
		public int QuestionnaireID
		{
			get { return questionnaireID; }
			set { SetDirty(ref questionnaireID, value); }
		}


		/// <summary>
		/// Gets or sets the questionnaire object this version pertains to
		/// </summary>
		public Questionnaire Questionnaire
		{
			get
			{
				if (questionnaire == null)
				{
					if (QuestionnaireID != 0)
					{
						questionnaire = new Questionnaire(CachePolicy);
						questionnaire.Load(Utility.DataAccess.GetConnectionString(), QuestionnaireID);
					}
				}
				return questionnaire;
			}
			set { questionnaire = value; }
		}


		/// <summary>
		/// Gets or sets the description for the questionnaire version
		/// </summary>
		public string Description
		{
			get { return description; }
			set { SetDirty(ref description, value); }
		}


		/// <summary>
		/// Gets or sets whether or not the questionnaire version is deployed (ever made active)
		/// </summary>
		public bool IsDeployed
		{
			get { return isDeployed; }
			set { SetDirty(ref isDeployed, value); }
		}


		/// <summary>
		/// Gets or sets the items(questions) collection of the questionnaire version
		/// </summary>
		public ArrayList Items
		{
			get
			{
				if (items == null)
				{
					items = QuestionnaireItem.GetQuestionnaireVersionItems(CachePolicy, Id);
				}
				return items;
			}
			set { items = value; }
		}


		/// <summary>
		/// Gets or sets the documents collection of the questionnaire version
		/// </summary>
		public ArrayList Documents
		{
			get
			{
				if (documents == null)
				{
					documents = QuestionDocument.GetQuestionDocumentsForVersion(CachePolicy, Id);
				}
				return documents;
			}
			set { documents = value; }
		}


		/// <summary>
		/// Gets or sets the scripts collection of the questionnaire version
		/// </summary>
		public Hashtable Scripts
		{
			get
			{
				if (scripts == null)
				{
					scripts = GetQuestionnaireVersionScripts(CachePolicy);
				}
				return scripts;
			}
			set
			{
				scripts = value;
				dirty = true;
			}
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Cache Key uniquely identifies this object within the cache.
		/// </summary>
		/// <returns>the unique string for this object.</returns>
		public override string CacheKey()
		{
			return QuestionnaireVersionKey;
		}


		/// <summary>
		/// Cache this instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(QuestionnaireVersionKey, this);
		}


		/// <summary>
		/// Remove this instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(QuestionnaireVersionKey);
		}


		/// <summary>
		/// Determines if the object is dirty based on whether or not atleast one of its properties have changed or any of its contained objects have changed
		/// </summary>
		/// <returns>true - if the object is dirty or any of its contained objects are dirty, false - if not.</returns>
		public override bool IsDirty()
		{
			if (Dirty)
				return true;

			if (items != null)
			{
				foreach (QuestionnaireItem o in items)
				{
					if (o.IsDirty())
						return true;
				}
			}
			if (documents != null)
			{
				foreach (QuestionDocument o in documents)
				{
					if (o.IsDirty())
						return true;
				}
			}
			if (scripts != null)
			{
				foreach (Script o in scripts.Values)
				{
					if (o.IsDirty())
						return true;
				}
			}
			return false;
		}


		/// <summary>
		/// Loads the QuestionnaireVersion object based on the id passed in as parameter
		/// </summary>
		/// <param name="connectionString">Information to connect to the database</param>
		/// <param name="args">QuestionnaireVersion Id of the object to be loaded</param>
		public override void Load(string connectionString, params object[] args)
		{

			if (args.Length != 1)
				throw new Exception("QuestionnaireVersion.Load expects ID only.");

			try
			{
				DataSet newDataSet = Utility.DataAccess.GetDataSet(connectionString, "GetQuestionnaireVersion",
					Utility.DataAccess.BuildSqlParameterArray("@id", SqlDbType.Int, (int)args[0]));
				if (newDataSet.Tables.Count > 0)
				{
					DataRowCollection rows = newDataSet.Tables[0].Rows;
					if (rows.Count == 1)
						PopulateDataMembers(rows[0]);
					else
						throw new Exception("No version with the specified id exists in the database");
				}
				else
				{
					throw new Exception("GetQuestionnaireVersion stored procedure returned incorrect results");
				}
			}
			catch (Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog("Failed to load QuestionnaireVersion object", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Failed to load QuestionnaireVersion object", ex);
			}
		}


		/// <summary>
		/// Saves the state of the QuestionnaireVersion object to the database. 
		/// The stored procedure inserts a new record if the id of the object is 0, 
		/// otherwise the procedure updates the record with the matching questionnaireversion id
		/// </summary>
		/// <param name="sqlTransaction">The database context in which the underlying sql statement needs to be executed</param>
		protected override void SaveObject(SqlTransaction sqlTransaction)
		{
			try
			{
				Id = Convert.ToInt32(Utility.DataAccess.ExecuteScalar(sqlTransaction.Connection, sqlTransaction, "SaveQuestionnaireVersion",
					Utility.DataAccess.BuildSqlParameterArray(
					"@id", SqlDbType.Int, Id
					, "@questionnaireID", SqlDbType.Int, questionnaireID
					, "@version", SqlDbType.Int, version
					, "@active", SqlDbType.Bit, isActive
					, "@description", SqlDbType.VarChar, description
					, "@published", SqlDbType.Bit, isDeployed
					, "@clone", SqlDbType.Bit, !IsClone)));

				if (items != null)
				{
					ArrayList deletedItemsList = new ArrayList();
					foreach (QuestionnaireItem item in items)
					{
						item.VersionId = Id;
						item.Save(sqlTransaction);
						//store the names of the items that are deleted
						if (item.MarkForDelete)
						{
							deletedItemsList.Add(item);
						}
					}
					//remove deleted items from items collection
					foreach (QuestionnaireItem item in deletedItemsList)
						items.Remove(item);
				}
				if (documents != null)
				{
					//Saving Document objects
					//Documents could be marked as deleted. The following logic will remove deleted Documents from the collection 
					//and as such a foreach loop is not used. The variable 'index' is a pointer
					//to the object in the collection that is being processed. The 'count' keeps track of the number of objects in 
					//the collection. The count will change whenever there is an object that is marked for deletion.
					int index = 0;
					int count = documents.Count;
					while (index < count)
					{
						QuestionDocument document = (QuestionDocument)documents[index];
						document.VersionId = Id;
						document.Save(sqlTransaction);

						if (!document.MarkForDelete)
						{
							++index;
						}
						else
						{
							documents.RemoveAt(index);
							--count;
						}
					}
				}
				if (scripts != null)
				{
					StringBuilder deletedScriptsList = new StringBuilder();
					//Deletion of scripts is handled here because before deleting the scripts, the Script_QuestionnaireVersion_Map table
					//has to be updated appropriately.
					//Insertion/Deletion of Script_QuestionnaireVersion_Map records also cannot be pushed to Script because Scripts can be tied to other tables.
					foreach (Script script in scripts.Values)
					{
						if (!script.MarkForDelete)
						{
							int scriptIdBeforeSave = script.Id;
							script.Save(sqlTransaction);
							if (scriptIdBeforeSave == 0)
							{
								Utility.DataAccess.ExecuteScalar(sqlTransaction.Connection, sqlTransaction, "SaveScriptQuestionnaireVersionMap",
									Utility.DataAccess.BuildSqlParameterArray(
									"@questionnaireVersionId", SqlDbType.Int, Id
									, "@scriptId", SqlDbType.Int, script.Id
									));
							}
						}
						else
						{
							Utility.DataAccess.ExecuteScalar(sqlTransaction.Connection, sqlTransaction, "DeleteScriptQuestionnaireVersionMap",
								Utility.DataAccess.BuildSqlParameterArray(
								"@questionnaireVersionId", SqlDbType.Int, Id
								, "@scriptId", SqlDbType.Int, script.Id
								));
							script.Delete(sqlTransaction);
							deletedScriptsList.Append(string.Format("{0};", script.Type));
						}
					}
					//remove deleted items from items collection
					string[] deletedScripts = deletedScriptsList.ToString().Split(';');
					for (int i = 0, count = deletedScripts.Length - 1; i < count; ++i)
						scripts.Remove(deletedScripts[i]);

				}
			}
			catch (Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog("QuestionnaireVersion.SaveObject: Failed to save the QuestionnaireVersion object to the database", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("QuestionnaireVersion.SaveObject: Failed to save the QuestionnaireVersion object to the database", ex);
			}

		}


		/// <summary>
		/// Deletes the record represented by the object from the database
		/// <para>This method is not yet implemented</para> 
		/// </summary>
		/// <param name="sqlTransaction">The database context in which the underlying sql statement needs to be executed</param>
		protected override void DeleteObject(SqlTransaction sqlTransaction)
		{
			throw new Exception("Not Implemented");
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Populates instance fields with corresponding values in the datarow
		/// </summary>
		/// <param name="row">DataRow holding the values to be assigned to instance variables</param>
		private void PopulateDataMembers(DataRow row)
		{
			object o = row[ColumnNameDescription];
			description = (o == DBNull.Value) ? string.Empty : (string)o;
			version = (int)row[ColumnNameVersion];
			isActive = (bool)row[ColumnNameActive];
			isDeployed = (bool)row[ColumnNamePublished];
			questionnaireID = (int)row[ColumnNameQuestionnaireId];
			Id = (int)row[ColumnNameId];
		}


		/// <summary>
		/// Adds all the child questionnaire items of a questionnaire item to its corresponding formitem
		/// </summary>
		/// <param name="parentFormItem">The FormItem object to which the QuestionnaireItem's have to be added</param>
		/// <param name="parentItem">The parent of the child QuestionnaireItem's which have to be added to a FormItem</param>
		private void AddChildFormItems(FormItem parentFormItem, QuestionnaireItem parentItem)
		{
			DataRow[] rows = itemsForForm.Select(string.Format("ParentName = '{0}'", parentItem.Name), "Order");
			FormItem childFormItem;
			QuestionnaireItem childItem;
			foreach (DataRow row in rows)
			{
				childItem = GetQuestionnaireItemById((int)row["ID"]);
				childFormItem = new FormItem(childItem);
				parentFormItem.FormItems.Add(childFormItem);
				AddChildFormItems(childFormItem, childItem);
			}
		}


		/// <summary>
		/// Populates FormItem objects with answers, if any available.
		/// </summary>
		/// <param name="formItem">FormItem to which the answers have to be added</param>
		/// <param name="itemAnswers">A collection of Answer objects</param>
		private void AddAnswers(FormItem formItem, ArrayList itemAnswers)
		{
			foreach (Answer itemAnswer in itemAnswers)
			{
				if (formItem.Name == itemAnswer.Name)
				{
					if (formItem.Type == QuestionnaireItem.CheckBoxListItemType)
						formItem.AddCheckBoxAnswer(itemAnswer.Value, itemAnswer.Id);
					else if (formItem.Type == QuestionnaireItem.MultiSelectItemType 
								|| formItem.Type == QuestionnaireItem.DropDownMultiSelectItemType 
								|| formItem.Type == QuestionnaireItem.CustomValueSelectorItemType)
						formItem.AddMultiSelectAnswer(itemAnswer.Value, itemAnswer.Id);
					else
						formItem.AddOrReplaceAnswer(itemAnswer.Value, itemAnswer.Id);
					foreach (FormItem childFormItem in formItem.FormItems)
					{
						if (itemAnswer.ChildAnswers != null && childFormItem.ParentValue == itemAnswer.Value)
							AddAnswers(childFormItem, itemAnswer.ChildAnswers);
					}
				}
			}
		}

		#endregion

		#region Static methods
		/// <summary>
		/// Gets all the versions of a Questionnaire with the specified id
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="questionnaireID">Id of the Questionnaire of which the versions have to be fetched</param>
		/// <returns>A hashtable of QuestionnaireVersion objects with VersionId as the key and the object as the id</returns>
		public static ArrayList GetQuestionnaireVersions(ICachePolicy cachePolicy, int questionnaireID)
		{
			ArrayList versionList = new ArrayList();
			try
			{
				DataSet ds = Utility.DataAccess.GetDataSet(Utility.DataAccess.GetConnectionString(), "GetQuestionnaireVersions",
					Utility.DataAccess.BuildSqlParameterArray
					("@questionnaireID", SqlDbType.Int, questionnaireID));
				if (ds.Tables.Count > 0)
				{
					foreach (DataRow row in ds.Tables[0].Rows)
					{
						QuestionnaireVersion version = new QuestionnaireVersion(cachePolicy, row);
						versionList.Add(version);
					}
				}
				else
				{
					throw new Exception("GetQuestionnaireVersions stored procedure returned incorrect results");
				}
			}
			catch (Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog("Failed to load Questionnaire Versions", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Failed to load Questionnaire Versions", ex);
			}

			return versionList;
		}

		#endregion

		#region Public methods
		/// <summary>
		/// Gets the scripts associated with the QuestionnaireVersion object. 
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="args">Optional script type</param>
		/// <returns>A hashtable of scripts with type as the key and the object as the value</returns>
		public Hashtable GetQuestionnaireVersionScripts(ICachePolicy cachePolicy, params object[] args)
		{
			Hashtable scripts = new Hashtable();
			DataSet versionScripts = null;
			try
			{
				if (args.Length > 1)
				{
					throw new Exception("GetQuestionnaireVersionScripts takes at most two parameters(cachePolicy, optional type)");
				}
				else if (args.Length == 1)
				{
					versionScripts = Utility.DataAccess.GetDataSet(Utility.DataAccess.GetConnectionString(), "GetQuestionnaireVersionScripts",
						Utility.DataAccess.BuildSqlParameterArray("@versionId", SqlDbType.Int, Id
						, "@type", SqlDbType.VarChar, args[0]));
				}
				else
				{
					versionScripts = Utility.DataAccess.GetDataSet(Utility.DataAccess.GetConnectionString(), "GetQuestionnaireVersionScripts",
						Utility.DataAccess.BuildSqlParameterArray("@versionId", SqlDbType.Int, Id));
				}

				if (versionScripts.Tables.Count > 0)
				{
					foreach (DataRow row in versionScripts.Tables[0].Rows)
					{
						ScriptEngine.Script script = new Script(cachePolicy, row);
						scripts.Add(script.Type, script);
					}
				}
				else
				{
					throw new Exception("GetQuestionnaireVersionScripts stored procedure returned incorrect results");
				}
			}
			catch (Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog("Failed to Load QuestionnaireVersion Scripts", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Failed to Load QuestionnaireVersion Scripts", ex);
			}

			return scripts;
		}


		/// <summary>
		/// Gets QuestionnaireItem objects with the specified name
		/// </summary>
		/// <param name="questionnaireItemName">Name of the questionnaireitem</param>
		/// <returns>Collection of QuestinnaireItem objects</returns>
		public Hashtable GetItems(string questionnaireItemName)
		{
			Hashtable questionnaireItemList = new Hashtable();
			foreach (QuestionnaireItem item in items)
			{
				if (item.ParentName == questionnaireItemName)
				{
					questionnaireItemList.Add(item.Id, item);
				}
			}
			return questionnaireItemList;
		}


		/// <summary>
		/// Converts the QuestionnaireVersion to a collection of objects which can be serialized. This method takes an optional 
		/// QuestionnaireAnswer as a parameter.
		/// 
		/// This method builds the Form object without the answers first and then for each FormItem within the Form object, 
		/// it loads the answers from the QuestionnaireAnswer object, if any, passed in as a parameter. The reason behind having another
		/// loop for loading answers is because of the MultiInput and DropDownMultiSelect questions. If these questions have any children
		/// associated with them, then FormItem objects representing the child questions are added to the parent FormItem. But for each answer, 
		/// the child FormItem object(s) are replicated and added to the parent FormItem leaving the default ones intact. This is especially
		/// useful in scenarios where the nesting for MultiSelect question is more than 1 level deep.
		/// </summary>
		/// <param name="args">Optional questionnaire answer object</param>
		/// <returns>Form object that can be serialized</returns>
		public Form GetForm(params object[] args)
		{
			if (args.Length > 1)
			{
				throw new Exception("Too many arguments. GetForm takes an optional QuestionnaireAnswer object as a parameter");
			}

			QuestionnaireAnswer questionnaireAnswer = null;
			if (args.Length == 1)
			{
				questionnaireAnswer = args[0] as QuestionnaireAnswer;
			}

			Questionnaire questionnaire = new Questionnaire(CachePolicy);
			questionnaire.Load(Utility.DataAccess.ConnectionString, questionnaireID);

			Form form = new Form();
			form.Name = questionnaire.Name;

			if (questionnaireAnswer != null)
			{
				form.Answer = questionnaireAnswer.Name;
				form.AllowMultiple = questionnaire.AllowMultiple;
				if (!string.IsNullOrEmpty(questionnaireAnswer.LastModifiedDate))
					form.DateLastModified = Convert.ToDateTime(questionnaireAnswer.LastModifiedDate).ToString("MMMM dd, yyyy");

				if ((int)QuestionnaireAnswer.UserAction.Remove == questionnaireAnswer.Action)
				{
					form.Remove = true;
				}
			}

			if (null == questionnaireAnswer ||
				 (int)QuestionnaireAnswer.UserAction.Remove != questionnaireAnswer.Action)
			{
				itemsForForm = new DataTable();
				itemsForForm.Columns.Add("ID", typeof(int));
				itemsForForm.Columns.Add("ParentName");
				itemsForForm.Columns.Add("Order", typeof(int));

				foreach (QuestionnaireItem o in Items)
				{
					DataRow itemsForFormRow = itemsForForm.NewRow();
					itemsForFormRow[ColumnNameId] = o.Id;
					itemsForFormRow[ColumnNameParentName] = o.ParentName;
					itemsForFormRow[ColumnNameOrder] = o.Order;
					itemsForForm.Rows.Add(itemsForFormRow);
				}

				//selecting questions without any parent 
				DataRow[] rows = itemsForForm.Select("IsNull(ParentName, '') = ''", "Order");

				//foreach toplevel question
				foreach (DataRow row in rows)
				{
					QuestionnaireItem item = GetQuestionnaireItemById((int)row[ColumnNameId]);
					FormItem formItem = new FormItem(item);
					form.FormItems.Add(formItem);
					AddChildFormItems(formItem, item);
				}

				if (null != questionnaireAnswer)
				{
					foreach (FormItem childFormItem in form.FormItems)
					{
						AddAnswers(childFormItem, questionnaireAnswer.GetAnswers(childFormItem.Name));
					}
				}
			}

			return form;
		}

		public QuestionnaireItem GetQuestionnaireItemById(int Id)
		{
			foreach (QuestionnaireItem item in Items)
			{
				if (item.Id == Id)
					return item;
			}
			Utility.ExceptionReport.WriteToEventLog("QuestionnaireVersion.GetQuestionnaireItemById", "Failed to find QuestionnaireItem", Utility.ExceptionReport.ReportType.Error);
			throw new Exception("QuestionnaireVersion.GetQuestionnaireItemById: Failed to find QuestionnaireItem");
		}

		/// <summary>
		/// Checks if a questionnaire item with the specified name already exists
		/// </summary>
		/// <param name="name">The name to be verified</param>
		/// <returns>true, if a QuestionnaireItem object with the specified name already exists</returns>
		public bool DoesItemNameExists(string name)
		{
			foreach (QuestionnaireItem item in Items)
			{
				if (item.Name.ToLower() == name.ToLower())
					return true;
			}
			return false;
		}


		/// <summary>
		/// Returns the highest order assigned to a QuestionnaireItem
		/// </summary>
		/// <returns></returns>
		public int GetMaxItemOrder()
		{
			int maxItemOrder = 0;
			foreach (QuestionnaireItem item in Items)
			{
				if (maxItemOrder < item.Order)
					maxItemOrder = item.Order;
			}
			return maxItemOrder;
		}
		#endregion
	}
	#endregion
}
